#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QModbusTcpClient>
#include <QModbusRtuSerialMaster>
#include <QDebug>
#include <QUrl>
#include <QTimer>
#include <QDateTime>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    m_settingsDialog = new SettingsDialog(this);

    initActions();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::initActions()
{
    ui->actionConnect->setEnabled(true);
    ui->actionDisconnect->setEnabled(false);
    ui->actionExit->setEnabled(true);
    ui->actionOptions->setEnabled(true);

    // 状态栏：通讯
    ComStatic = new QLabel("通讯端口状态", this);
    ComStatic->setMinimumWidth(150);
    ComStatic->setAlignment(Qt::AlignCenter);  //水平居中(HCenter)。
    statusBar()->addPermanentWidget(ComStatic); //现实永久信息
    statusBar()->showMessage(this->windowTitle());

    ledLabel = new QLabel();
    ledLabel->setAlignment(Qt::AlignCenter);  //水平居中(HCenter)。
    const QString m_grey_SheetStyle = "min-width: 16px; min-height: 16px;max-width:16px; max-height: 16px;border-radius: 8px;  border:1px solid black;background:grey";
    ledLabel->setStyleSheet(m_grey_SheetStyle);//改成 灰色圆形
    statusBar()->addPermanentWidget(ledLabel);

    // 状态栏：时钟
    ClockStatic = new QLabel("", this);
    ClockStatic->setMinimumWidth(130);
    ClockStatic->setAlignment(Qt::AlignCenter);  //水平居中(HCenter)。
    statusBar()->addPermanentWidget(ClockStatic); //现实永久信息
    clocktime = new QTimer(this);
    connect(clocktime, &QTimer::timeout,[this]() {
        QDateTime t = QDateTime::currentDateTime();
        ClockStatic->setText(t.toString("yyyy-MM-dd hh:mm:ss"));
    });
    clocktime->start(1000);

    // 状态栏：版权
    QLabel *Copyright = new QLabel("<font>Copyright&#169; 2019-2020</font> 中国.武汉", this);
    Copyright->setMinimumWidth(250);
    Copyright->setAlignment(Qt::AlignCenter);  //水平居中(HCenter)。
    statusBar()->addPermanentWidget(Copyright); //现实永久信息

    connect(ui->actionConnect, &QAction::triggered,
            this, &MainWindow::on_actionConnect_triggered);
    connect(ui->actionDisconnect, &QAction::triggered,
            this, &MainWindow::on_actionConnect_triggered);

    connect(ui->actionExit, &QAction::triggered, this, &QMainWindow::close);
    connect(ui->actionOptions, &QAction::triggered, m_settingsDialog, &QDialog::show);
}

// 连接从机
void MainWindow::connectSlave(ModbusConnection t)
{
    if (modbusDevice) {
        modbusDevice->disconnectDevice();
        delete modbusDevice;
        modbusDevice = nullptr;
    }

    auto type = static_cast<ModbusConnection>(t);
    if (type == Serial) {
#if QT_CONFIG(modbus_serialport)
        modbusDevice = new QModbusRtuSerialMaster(this);
#endif
    } else if (type == Tcp) {
        modbusDevice = new QModbusTcpClient(this);
        if (m_settingsDialog->settings().ip.isEmpty())
            m_settingsDialog->settings().ip = QLatin1String("127.0.0.1:502");
    }

    connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
        statusBar()->showMessage(modbusDevice->errorString(), 5000);
    });

    if (!modbusDevice) {

        if (type == Serial)
            statusBar()->showMessage(tr("Could not create Modbus master."), 5000);
        else
            statusBar()->showMessage(tr("Could not create Modbus client."), 5000);
    } else {
        connect(modbusDevice, &QModbusClient::stateChanged,
                this, &MainWindow::onModbusStateChanged);
    }
}

void MainWindow::on_actionConnect_triggered()
{
    if (!modbusDevice)
        return;

    statusBar()->clearMessage();
    if (modbusDevice->state() != QModbusDevice::ConnectedState) {
        if (static_cast<ModbusConnection>(m_settingsDialog->settings().connectType) == Serial) {
            modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                m_settingsDialog->settings().port);
#if QT_CONFIG(modbus_serialport)
            modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
                m_settingsDialog->settings().parity);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
                m_settingsDialog->settings().baud);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
                m_settingsDialog->settings().dataBits);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
                m_settingsDialog->settings().stopBits);
#endif
        } else {
            const QUrl url = QUrl::fromUserInput(m_settingsDialog->settings().ip);
            modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
            modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());
        }
        modbusDevice->setTimeout(m_settingsDialog->settings().responseTime);
        modbusDevice->setNumberOfRetries(m_settingsDialog->settings().numberOfRetries);
        if (!modbusDevice->connectDevice()) {
            statusBar()->showMessage(tr("Connect failed: ") + modbusDevice->errorString(), 5000);
        } else {
            ui->actionConnect->setEnabled(false);
            ui->actionDisconnect->setEnabled(true);
        }
    } else {
        modbusDevice->disconnectDevice();
        ui->actionConnect->setEnabled(true);
        ui->actionDisconnect->setEnabled(false);
    }
}

void MainWindow::onModbusStateChanged(int state)
{
    bool connected = (state != QModbusDevice::UnconnectedState);
    ui->actionConnect->setEnabled(!connected);
    ui->actionDisconnect->setEnabled(connected);

    if (state == QModbusDevice::UnconnectedState)
        qDebug() << tr("Connect");
    else if (state == QModbusDevice::ConnectedState)
        qDebug() << tr("Disconnect");
}

QModbusDataUnit MainWindow::readRequest(int type, int startAddress, int readSize) const
{
    const auto table =
        static_cast<QModbusDataUnit::RegisterType>(type);

    return QModbusDataUnit(table, startAddress, readSize);
}

// 向从机写数据
void MainWindow::readWriteMaster(int regType, int startAddress, int readSize)
{
    if (!modbusDevice)
        return;

    statusBar()->clearMessage();

    const auto table =
        static_cast<QModbusDataUnit::RegisterType>(regType);

    // 构建要向从机写的数据结构
    QModbusDataUnit writeUnit =QModbusDataUnit(table, startAddress, readSize);;
    for (int i = 0, total = int(writeUnit.valueCount()); i < total; ++i) {
        writeUnit.setValue(i, hReg.byte2[i + writeUnit.startAddress()]);
    }

    // 向从机写数据，并返回从机新数据
    if (auto *reply = modbusDevice->sendReadWriteRequest(readRequest(regType, startAddress, readSize), writeUnit,
        m_settingsDialog->settings().serverAddr)) {
        if (!reply->isFinished())
            connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);
        else
            delete reply; // broadcast replies return immediately
    } else {
        statusBar()->showMessage(tr("Read error: ") + modbusDevice->errorString(), 5000);
    }
}

// 读从机数据
void MainWindow::readMaster(int regType, int startAddress, int readSize)
{
    if (!modbusDevice)
        return;

    statusBar()->clearMessage();

    if (auto *reply = modbusDevice->sendReadRequest(readRequest(regType, startAddress, readSize),
                                                    m_settingsDialog->settings().serverAddr)) {
        if (!reply->isFinished())
            connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);
        else
            delete reply; // broadcast replies return immediately
    } else {
        statusBar()->showMessage(tr("Read error: ") + modbusDevice->errorString(), 5000);
    }
}

void MainWindow::onReadReady()
{
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    if (reply->error() == QModbusDevice::NoError) {
        const QModbusDataUnit unit = reply->result();
        for (int i = 0, total = int(unit.valueCount()); i < total; ++i) {
            const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i)
                                     .arg(QString::number(unit.value(i),
                                          unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16));
            qDebug() << entry;
        }
    } else if (reply->error() == QModbusDevice::ProtocolError) {
        statusBar()->showMessage(tr("Read response error: %1 (Mobus exception: 0x%2)").
                                    arg(reply->errorString()).
                                    arg(reply->rawResult().exceptionCode(), -1, 16), 5000);
    } else {
        statusBar()->showMessage(tr("Read response error: %1 (code: 0x%2)").
                                    arg(reply->errorString()).
                                    arg(reply->error(), -1, 16), 5000);
    }

    reply->deleteLater();
}


